#pragma once
#include <stdio.h>
#include <cassert>
#include <memory>
#include <thread>
#include "krpc/netserver.h"
#include "krpc/service.h"
#include "krpc/access.h"
#include "rapidjson/meta.h"
#include "rapidjson/deserialize.hpp"

struct ServiceConf
{
	uint16_t 					port = 0;	//tcp或者udp服务端口号
	std::string 				filename;		//unix域stream或者dgram套接字文件名
	std::string 				protocol;	//服务所使用的协议,目前只支持tcp和udp
	std::list<KRPC::FuncConf> 	func;		//服务所提供的接口函数配置
	std::set<std::string> 		publish;	//服务所发布的主题

	METAS(port, filename, protocol, func, publish);
	MSGPACK_DEFINE(port, filename, protocol, func, publish);
};

struct RpcConf
{
	std::string 			version;				//程序版本号
	std::string 			program;				//程序名
	std::list<ServiceConf> 	service;				//程序所提供的服务配置

	METAS(version, program, service);
	MSGPACK_DEFINE(version, program, service);
};

class RPC
{
public:
	explicit RPC(const std::string &confStr)
	{
		assert(rapidjson::deserialize(conf, confStr));
		init(conf.service);
	}
	RPC(const std::string &str, std::list<ServiceConf>& service)
	{
		conf.version = str;
		init(service);
	}
	void init(std::list<ServiceConf>& service) 
	{
		std::set<std::string> funcnames;
		for (auto &sc : service) {
			if (sc.protocol != "tcp" && sc.protocol != "udp" && sc.protocol != "localstream" && sc.protocol != "localdgram")
				continue;
			for (auto &fc : sc.func) {
				assert(funcnames.find(fc.name) == funcnames.end());	//断言RPC配置中存在同样命名的函数
				funcnames.insert(fc.name);
			}
			saaslist.emplace_back(sc);
		}
		bind("getVersion", [this]()->std::string{return conf.version;});
		//bind("getConf", [this]()->RpcConf{return conf;});//注意添加此接口时需要在RpcConf结构体中添加MSGPACK_DEFINE()声明
	}

	template<typename ... Args>
	void bind(std::string funcname, Args&&... args)
	{
		//bool exist = false;
		for (auto &saas : saaslist) {
			for (auto &fc : saas.conf.func) {
				if (fc.name == funcname) {
					assert(!fc.binded);	//断言注册函数名不重复
					saas.service.bind(std::move(funcname), std::forward<Args>(args)...);
					fc.binded = true;
					return;
				}
			}
		}
	}

	void run()
	{
		auto count = saaslist.size();
		if (count == 1) {
			saaslist.begin()->service.run();
		} else if (count > 1) {
			std::list<std::thread> threads;
			for (auto it = std::next(saaslist.begin()); it != saaslist.end(); it++) {
				threads.emplace_back([it]{it->service.run();});
			}
			saaslist.begin()->service.run();
		} else {
			exit(1);	//说明一个服务也没有
		}
	}

	template<typename ... Args>
	void publish(const std::string &topic, Args&&... args)
	{
		for (auto &saas : saaslist) {
			if (saas.conf.publish.find(topic) != saas.conf.publish.end()) {
				saas.service.publish(topic, std::forward<Args>(args)...);
				return;
			}
		}
	}

private:
	struct Saas 	//software as a service
	{
		ServiceConf 					&conf;
		KRPC::Access 					access;
		std::unique_ptr<KRPC::Server> 	server;
		KRPC::Service 					service;
		
		static KRPC::Server *newServer(ServiceConf &sc, KRPC::Access &access)
		{
			if (sc.protocol == "tcp") {
				return new KRPC::NetServer<KRPC::ServerType::TCP>(sc.port, access);
			} else if (sc.protocol == "udp") {
				return new KRPC::NetServer<KRPC::ServerType::UDP>(sc.port, access);
			} else if (sc.protocol == "localstream") {
				return new KRPC::NetServer<KRPC::ServerType::LOCALSTREAM>(sc.filename, access);
			} else {	//sc.protocol == "localdgram"
				return new KRPC::NetServer<KRPC::ServerType::LOCALDGRAM>(sc.filename, access);
			}
		}
		 
		Saas(ServiceConf &sc) : \
			conf(sc), access(sc.func, sc.publish), \
			server(newServer(sc, access)), \
			service(*(server.get())) {}

		/*bool operator()(const std::string &funcname)
		{
			for (auto &fc : conf.func) {
				if (fc.name == funcname)
					return true;
			}
			return false;
		}*/
	};
	std::list<Saas> saaslist;
	RpcConf conf;
};
